(function(){
    var root;
    var listenerList = [];
    var Hash = {};
    function hash(hash){
        location.href = location.protocol + "//" + location.host + location.pathname + location.search + "#" + hash;
    }
    function hashPath(path, params){
        var str = "";
        for (var key in params) {
            str +=  "^" + key + "_" + params[key];
        }
        if(str !== ""){
            str = "$" + str.substring(1);
        }
        hash(path + str);
    }
    function addListener(path, func){
        listenerList.push({path: path, func:func});
    }
    function addTemplate(path, template){
        this.addListener({path: path, func:function(){return root.innerHTML =  template;}});
    }

    function getArrayDefault(array, index, defaultValue){
        if ( (typeof index) === "number" ){
            if (array.length <= index){
                return defaultValue;
            }

        }
        else if ( (typeof index) !== "string"){
            return defaultValue;
        }
        var value = array[index];
        var r = undefined === value || null === value || value === "";
        if (r) {
            return defaultValue;
        }
        return value;
    }
    function Paramters(params){
        return {
            get: function(key){
                var val = this.getArray(key);
                if (val.length > 0) {
                    return val[0];
                }
                return undefined;
            },
            getArray: function(key){
                var arr = params[key];
                if (arr === undefined) {
                    return [];
                }
                return arr;
            }
        }
    }
    function getParameter(search){
        var KVOBJ = {};
        var kvs = search.split("^");
        if (kvs.length > 0){
            for (var i = 0, l = kvs.length; i < l; i ++){
                var kv = kvs[i].split("_");
                var key = getArrayDefault(kv, 0, "");
                if ( key !== ""){
                    var value = getArrayDefault(kv, 1, "");
                    var VOBJ = getArrayDefault(KVOBJ, key, []);
                    value = decodeURI(value);
                    VOBJ.push(value);
                    KVOBJ[key] = VOBJ;
                }
            }
        }
        return KVOBJ;
    }
    window.addEventListener("hashchange", function(e){
        var url = new URL(e.newURL);
        var hash = url.hash;
        if (undefined == hash || null == hash || "" == hash){
            return;
        }
        hash = hash.substring(1);
        var index = hash.indexOf("$");
        var path = hash;
        var params = {};
        if (index !== -1) {
            path = hash.substring(0, index);
            params = getParameter(hash.substring(index+1));
        }
        var item = null;
        item = listenerList.find(function (item){
            return path === item.path;
        });
        if (item !== null) {
            item.func(e, new Paramters(params));
        }
    });

    Hash = {
        hash: hash,
        hashPath: hashPath,
        addListener: addListener,
        addTemplate: addTemplate,
        setContainer: function(container){
            root = container;
        },
        getParameter: function(){
            var hashStr = location.hash;
            hashStr = hashStr.substring(1);
            var params = "";
            var index = hashStr.indexOf("$");
            if (index !== -1) {
                params = getParameter(hashStr.substring(index+1));
            }
            return new Paramters(params);
        }
    }
    window.Hash = Hash;
})(window);
